/*
 *
 *  * Copyright (c) 2016. For Intelligent Group.
 *
 */

package com.intelligent.ispc.client.ex;

import com.google.common.collect.Lists;
import com.intelligent.ispc.client.core.view.MainPanel;
import com.intelligent.ispc.client.utils.AppMessageFactory;
import com.intelligent.ispc.client.utils.ApplicationBeanUtils;
import com.intelligent.ispc.client.utils.Colur;
import com.intelligent.ispc.client.utils.GBC;
import com.intelligent.ispc.client.utils.validation.Java2DIconFactory;
import com.intelligent.ispc.common.utils.ResourceBundleUtils;
import com.intelligent.ispc.common.utils.StringUtils;
import com.intelligent.ispc.utils.executor.ExecutorPool;

import javax.swing.*;
import javax.swing.text.DefaultCaret;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;

/**
 * Created by Peter on 2016/8/11.
 */
public class ProcessPanel extends JPanel {
    private List<ProcessTask> processTasks;
    private static final int STATUS_IN = 1;
    private static final int STATUS_OUT = 2;
    private int currentStatus = STATUS_OUT;
    private static final Dimension DIALOG_SIZE_FULL = new Dimension(350, 220);
    private static final Dimension DIALOG_SIZE_SMALL = new Dimension(350, 80);
    private static final int FINISH_POINT = 100;

    private long beginTime;
    private long endTime;
    private long runTime;
    private String url;

    /**
     * Constructor
     */
    public ProcessPanel() {
        initComponents();
    }

    /**
     * Check is process task whether is empty.
     *
     * @return true if exist.
     */
    public boolean isProcessTaskEmpty() {
        if (processTasks == null || processTasks.isEmpty()) {
            return true;
        }
        return false;
    }

    private void initComponents() {
        processTasks = Lists.newArrayList();
        outDialog = new JDialog();
        outDialog.setLayout(new GridBagLayout());
        outDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        outDialog.setPreferredSize(DIALOG_SIZE_FULL);
        outDialog.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                shutdownAllTask();
            }

            @Override
            public void windowClosing(WindowEvent e) {
                if (processTasks != null && !processTasks.isEmpty()) {
                    ProcessTask task = processTasks.get(0);
                    if (task.isSuccess()) {
                        int i = AppMessageFactory.showConfirmDialog("Close dialog will cancel all tasks, are you sure?");
                        if (i == AppMessageFactory.CANCEL_OPTION) {
                            return;
                        }
                        if (i == AppMessageFactory.YES_OPTION) {
                            ExecutorPool.shutDownNow();
                            processTasks.remove(processTasks.size() - 1);
                        }
                    } else {
                        processTasks.remove(processTasks.size() - 1);
                    }
                }
                url = null;
                super.windowClosing(e);
                outDialog.dispose();
            }
        });

        logArea = new JTextArea();
        logScroll = new JScrollPane(logArea);
        logScroll.setPreferredSize(outDialog.getPreferredSize());

        this.setLayout(new GridBagLayout());
        processMsgLabel = new JLabel("No details.");
    }

    /**
     * Create a new task to Process task list.
     *
     * @param name    task name
     * @param showLog is showing log area
     */
    public void addTask(String name, boolean showLog) {
        ProcessTask processTask = new ProcessTask();
        processTask.setTaskName(name);
        processTask.setTaskText(ResourceBundleUtils.getString(name));
        processTask.setCurrentPoint(0);
        processTask.setShowLog(showLog);
        for (int i = processTasks.size() - 1; i >= 0; i--) {
            if (processTasks.get(i).getTaskName().equals(name)) {
                processTasks.remove(i);
            }
        }
        processTasks.add(processTask);
        beginTime = System.currentTimeMillis();
        refreshOutDialog();
        if (showLog) {
            outDialog.setPreferredSize(DIALOG_SIZE_FULL);
        } else {
            outDialog.setPreferredSize(DIALOG_SIZE_SMALL);
        }
        showDialog();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (processTasks != null && !processTasks.isEmpty() && processTasks.get(0).isSuccess()) {
                    refreshOutDialog();
                    try {
                        final int sleepTime = 1000;
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();
    }

    /**
     * Update the  url property.
     *
     * @param name task name
     * @param url  url
     */
    public void updateUrl(String name, String url) {
        this.setUrl(url);
    }

    /**
     * Finish the task provided name, and remove from task list.
     *
     * @param name      task name
     * @param isVisible the dialog visibility setting.
     */
    public void finishTask(String name, boolean isVisible) {
        removeTask(name);
        if (!isVisible) {
            outDialog.setVisible(isVisible);
            outDialog.dispose();
        }
        btn.setEnabled(false);
    }

    /**
     * Remove the task provided name.
     *
     * @param name task name
     */
    public void removeTask(String name) {
        for (int i = processTasks.size() - 1; i >= 0; i--) {
            if (processTasks.get(i).getTaskName().equals(name)) {
                processTasks.remove(i);
            }
        }
        refreshOutDialog();
    }

    /**
     * Show alarm(error) status process with name.
     *
     * @param name task name
     * @param log  log
     */
    public void alarmTask(String name, String log) {
        for (int i = processTasks.size() - 1; i >= 0; i--) {
            if (processTasks.get(i).getTaskName().equals(name)) {
                processTasks.get(i).setSuccess(false);
                processTasks.get(i).setLog(log);
                processTasks.get(i).setShowLog(true);
                processTasks.get(i).setCurrentPoint(FINISH_POINT);
            }
        }
        refreshOutDialog();
        outDialog.setPreferredSize(new Dimension(((Double) outDialog.getSize().getWidth()).intValue(), ((Double) DIALOG_SIZE_FULL.getHeight()).intValue()));
        outDialog.pack();
    }

    /**
     * Update the task process point and log.
     *
     * @param name  task name
     * @param point point
     * @param log   log
     */
    public void updateTaskProcess(String name, Integer point, String log) {

        for (ProcessTask processTask : processTasks) {
            if (name.equals(processTask.getTaskName())) {
                processTask.updateProcessBar(point, log);
                for (int i = processTask.currentPoint; i < point; i++) {
                    processTask.updateProcessBar(i, null);
                    refreshOutDialog();
                }
                break;
            }
        }
        refreshOutDialog();
    }

    /**
     * Check the task is working whether not.
     *
     * @param name task name
     * @return true when working.
     */
    public boolean isWorking(String name) {
        if (name == null) {
            return false;
        }
        if (processTasks != null && !processTasks.isEmpty()) {
            return true;
        }
        return false;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * Process task class.
     */
    class ProcessTask {
        private String taskName;
        private String taskText;
        private int currentPoint = 0;
        private JProgressBar progressBar;
        private String log = "";

        //        private String url;
        private boolean isSuccess = true;
        private boolean showLog = false;

        /**
         * Constructor
         */
        ProcessTask() {
            progressBar = new JProgressBar();
            progressBar.setOrientation(SwingConstants.HORIZONTAL);
            progressBar.setValue(getCurrentPoint());
        }

        public void updateProcessBar(Integer point) {
            updateProcessBar(point, null);
        }

        public void updateProcessBar(Integer point, String log) {
            if (point != null) {
                if (point >= this.currentPoint) {
                    progressBar.setValue(point);

                }
                if (point == FINISH_POINT) {
                    progressBar.setBackground(Colur.LIGHTGREEN);
                    progressBar.setForeground(Colur.LIGHTGREEN);
                }
                this.currentPoint = point;
            }
            if (StringUtils.isNotBlank(log)) {
                this.log += log + "\n";
            }
        }

        public String getTaskName() {
            return taskName;
        }

        public void setTaskName(String taskName) {
            this.taskName = taskName;
        }

        public String getTaskText() {
            return taskText;
        }

        public void setTaskText(String taskText) {
            this.taskText = taskText;
        }

        public int getCurrentPoint() {
            return currentPoint;
        }

        public void setCurrentPoint(int currentPoint) {
            this.currentPoint = currentPoint;
            progressBar.setValue(currentPoint);
        }

        public JProgressBar getProgressBar() {
            return progressBar;
        }

        public void setProgressBar(JProgressBar progressBar) {
            this.progressBar = progressBar;
        }

        public boolean isSuccess() {
            return isSuccess;
        }

        public void setSuccess(boolean success) {
            isSuccess = success;
        }

        public String getLog() {
            return log;
        }

        public void setLog(String log) {
            this.log = log;
        }

        public boolean isShowLog() {
            return showLog;
        }

        public void setShowLog(boolean showLog) {
            this.showLog = showLog;
        }
    }

    private void shutdownAllTask() {

        for (int i = processTasks.size() - 1; i >= 0; i--) {
            ExecutorPool.getTask(processTasks.get(i).getTaskName()).shutdown();
            removeTask(processTasks.get(i).getTaskName());
        }
    }

    private void shutdownTask(String name) {
        if (StringUtils.isNotBlank(name)) {
            for (int i = processTasks.size() - 1; i >= 0; i--) {
                if (processTasks.get(i).getTaskName() != null && processTasks.get(i).getTaskName().equals(name)) {
                    ExecutorPool.getTask(name).shutdown();
                    removeTask(name);
                    break;
                }
            }
        }
    }

    private void refreshOutDialog() {
        if (!processTasks.isEmpty()) {
            outDialog.getContentPane().removeAll();

            JPanel jPanel = new JPanel(new GridBagLayout());

            for (int i = processTasks.size() - 1; i >= 0; i--) {
                JPanel barPanel = new JPanel(new GridBagLayout());
                barPanel.setName(String.valueOf(i));
                JLabel label = new JLabel(processTasks.get(i).getTaskText());
                Font font = new Font("", 1, 10);
                label.setFont(font);
                label.setBackground(Colur.BLUE);
                timeLabel = new JLabel();
                JProgressBar pbar = processTasks.get(i).getProgressBar();
                if (!processTasks.get(i).isSuccess()) {
                    pbar.setBackground(Colur.RED);
                    pbar.setForeground(Colur.RED);
                }

                BufferedImage image = Java2DIconFactory.createErrorIcon();
                btn = new JButton(new ImageIcon(image));
                btn.setBorderPainted(false);
                btn.setToolTipText("Cancel");

                btn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (processTasks != null && !processTasks.isEmpty()) {
                            String taskName = processTasks.get(Integer.valueOf(barPanel.getName())).getTaskName();
                            if (StringUtils.isBlank(taskName)) {
                                refreshOutDialog();
                            } else {
                                int i = AppMessageFactory.showConfirmDialog("Close dialog will cancel all tasks, are you sure?");
                                if (i == AppMessageFactory.CANCEL_OPTION) {
                                    return;
                                }
                                if (i == AppMessageFactory.YES_OPTION) {
                                    outDialog.setVisible(false);
                                    outDialog.dispose();
                                    ExecutorPool.shutDownNow();
                                    removeTask(taskName);
                                }
                            }
                        } else {
                            refreshOutDialog();
                        }
                    }
                });

                logArea.setEditable(false);
                logArea.setLineWrap(true);
                logArea.setWrapStyleWord(true);
                DefaultCaret caret = (DefaultCaret) logArea.getCaret();
                caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

                logScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
                logScroll.setVisible(true);
                logArea.setText(processTasks.get(i).getLog());
                if (processTasks.get(i).isSuccess() && !processTasks.get(i).isShowLog()) {
                    logScroll.setVisible(false);
                }
                final int xFull = 100;
                final int yFull = 100;

                barPanel.add(label, new GBC(0, 0).setFill(GBC.HORIZONTAL).setAnchor(GBC.NORTH).setWeight(xFull, 0));
                barPanel.add(timeLabel, new GBC(1, 0).setFill(GBC.HORIZONTAL));
                barPanel.add(pbar, new GBC(0, 1, 2, 1).setFill(GBC.HORIZONTAL).setAnchor(GBC.NORTH));
                barPanel.add(btn, new GBC(2, 1).setFill(GBC.HORIZONTAL));
                barPanel.add(logScroll, new GBC(0, 2, 2, 1).setFill(GBC.BOTH).setWeight(xFull, yFull).setInsets(3, 0, 0, 0));

                if (!StringUtils.isBlank(this.getUrl()) && processTasks.get(i).getCurrentPoint() == FINISH_POINT) {
                    JButton urlBtn = new JButton("Open Export folder");
                    String url = this.getUrl();
                    urlBtn.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            openPath(url);
                        }
                    });
                    barPanel.add(urlBtn, new GBC(0, 3, 1, 1).setAnchor(GBC.WEST).setWeight(0, 0).setInsets(3, 0, 0, 0));
                }

                barPanel.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        if (logScroll.isVisible()) {
                            logScroll.setVisible(false);
                        } else {
                            logScroll.setVisible(true);
                        }
                    }
                });
                if (processTasks.size() > 1 && i < processTasks.size() - 1) {
                    barPanel.add(new JSeparator(SwingConstants.HORIZONTAL), new GBC(0, 3).setFill(GBC.HORIZONTAL).setWeight(100, 0).setInsets(3, 0, 0, 0));
                }
                jPanel.add(barPanel, new GBC(0, i).setFill(GBC.BOTH).setWeight(100, 100));

            }

            endTime = System.currentTimeMillis();
            String times = formatRunTime();
            timeLabel.setText(times);

            outDialog.add(jPanel, new GBC(0, 0).setFill(GBC.BOTH).setWeight(100, 100).setInsets(5));
            outDialog.revalidate();
            outDialog.repaint();
        }
    }

    private String formatRunTime() {
        runTime = endTime - beginTime;
        Date date = new Date(runTime);
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
        format.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));
        String str = format.format(calendar.getTime());
        return str;
    }

    private void showDialog() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    outDialog.pack();
                    outDialog.setLocationRelativeTo(ApplicationBeanUtils.getBean(MainPanel.class));
                    outDialog.setModal(true);
                    outDialog.setVisible(true);
                    Thread.currentThread().interrupt();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        currentStatus = STATUS_OUT;
    }

    private void openPath(String path) {
        try {
            java.awt.Desktop.getDesktop().open(new File(path));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private JTextArea logArea;
    private JDialog outDialog;
    private JLabel processMsgLabel;
    private JLabel timeLabel;
    private JButton btn;
    private JScrollPane logScroll;
}
